package com.hanserwei.han_picture.manager;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hanserwei.han_picture.domain.entity.po.Picture;
import com.hanserwei.han_picture.utils.KryoSerializerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Redis管理器
 *
 * @author hanserwei
 */
@Component
@Slf4j
public class RedisManager {

    // Redis键前缀
    private static final String TAG_SCORE_KEY = "picture:tags:score";
    private static final String USER_SESSION_KEY = "user:session:";
    private final RedisTemplate<String, Object> redisTemplate;
    private final RedisTemplate<String, byte[]> kryoRedisTemplate;

    public RedisManager(RedisTemplate<String, Object> redisTemplate,
                        @Qualifier("kryoRedisTemplate") RedisTemplate<String, byte[]> kryoRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.kryoRedisTemplate = kryoRedisTemplate;
    }

    /**
     * 增加标签分数
     *
     * @param tag   标签
     * @param score 分数增量
     */
    public void incrementTagScore(String tag, double score) {
        try {
            redisTemplate.opsForZSet().incrementScore(TAG_SCORE_KEY, tag, score);
        } catch (Exception e) {
            log.error("增加标签分数失败: tag={}, score={}", tag, score, e);
        }
    }

    /**
     * 批量增加标签分数
     *
     * @param tagScoreMap 标签分数映射
     */
    public void incrementTagsScore(Map<String, Double> tagScoreMap) {
        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            tagScoreMap.forEach((tag, score) -> zSetOps.incrementScore(TAG_SCORE_KEY, tag, score));
        } catch (Exception e) {
            log.error("批量增加标签分数失败: tagScoreMap={}", tagScoreMap, e);
        }
    }


    /**
     * 批量增加标签分数
     *
     * @param tags  标签列表
     * @param score 每个标签的分数增量
     */
    public void incrementTagsScore(List<String> tags, double score) {
        if (tags == null || tags.isEmpty()) {
            return;
        }
        try {
            for (String tag : tags) {
                redisTemplate.opsForZSet().incrementScore(TAG_SCORE_KEY, tag, score);
            }
        } catch (Exception e) {
            log.error("批量增加标签分数失败: tags={}, score={}", tags, score, e);
        }
    }

    /**
     * 清除标签
     */
    public void decrementTagsScore(List<String> tags) {
        if (tags == null || tags.isEmpty()) {
            return;
        }
        try {
            for (String tag : tags) {
                Double currentScore = redisTemplate.opsForZSet().incrementScore(TAG_SCORE_KEY, tag, -1.0);
                // 如果分数小于等于0，则删除该标签
                if (currentScore != null && currentScore <= 0) {
                    redisTemplate.opsForZSet().remove(TAG_SCORE_KEY, tag);
                }
            }
        } catch (Exception e) {
            log.error("清除标签分数失败: tags={}", tags, e);
        }
    }

    /**
     * 清除所有标签
     */
    public void clearAllPictureTags() {
        try {
            redisTemplate.delete(TAG_SCORE_KEY);
        } catch (Exception e) {
            log.error("清除所有标签分数失败", e);
        }
    }

    /**
     * 获取前N个热门标签
     *
     * @param count 获取数量
     * @return 标签列表（按分数降序）
     */
    public List<String> getTopTags(int count) {
        try {
            Set<Object> tags = redisTemplate.opsForZSet().reverseRange(TAG_SCORE_KEY, 0, count - 1);
            return tags != null ? tags.stream().map(Object::toString).toList() : List.of();
        } catch (Exception e) {
            log.error("获取热门标签失败: count={}", count, e);
            return List.of();
        }
    }

    /**
     * 获取标签分数
     *
     * @param tag 标签
     * @return 分数
     */
    public Double getTagScore(String tag) {
        try {
            return redisTemplate.opsForZSet().score(TAG_SCORE_KEY, tag);
        } catch (Exception e) {
            log.error("获取标签分数失败: tag={}", tag, e);
            return 0.0;
        }
    }

    /**
     * 保存用户会话
     *
     * @param userId      用户ID
     * @param user        用户对象
     * @param expireHours 过期小时数
     */
    public void saveUserSession(Long userId, Object user, long expireHours) {
        try {
            String key = USER_SESSION_KEY + userId;
            redisTemplate.opsForValue().set(key, user, Duration.ofHours(expireHours));
        } catch (Exception e) {
            log.error("保存用户会话失败: userId={}", userId, e);
        }
    }

    /**
     * 获取用户会话
     *
     * @param userId 用户ID
     * @return 用户对象
     */
    public Object getUserSession(Long userId) {
        try {
            String key = USER_SESSION_KEY + userId;
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            log.error("获取用户会话失败: userId={}", userId, e);
            return null;
        }
    }

    /**
     * 删除用户会话
     *
     * @param userId 用户ID
     */
    public void deleteUserSession(Long userId) {
        try {
            String key = USER_SESSION_KEY + userId;
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("删除用户会话失败: userId={}", userId, e);
        }
    }

    /**
     * 检查用户会话是否存在
     *
     * @param userId 用户ID
     * @return 是否存在
     */
    public boolean hasUserSession(Long userId) {
        try {
            String key = USER_SESSION_KEY + userId;
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            log.error("检查用户会话失败: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 刷新用户会话过期时间
     *
     * @param userId      用户ID
     * @param expireHours 过期小时数
     */
    public void refreshUserSession(Long userId, long expireHours) {
        try {
            String key = USER_SESSION_KEY + userId;
            redisTemplate.expire(key, Duration.ofHours(expireHours));
        } catch (Exception e) {
            log.error("刷新用户会话失败: userId={}", userId, e);
        }
    }

    /**
     * 保存图片分页缓存
     *
     * @param key         缓存键
     * @param picturePage 图片分页数据
     */
    public void savePictureCache(String key, Page<Picture> picturePage) {
        try {
            // 使用专门的 Kryo Redis 模板来处理 byte[] 数据
            byte[] serializedData = KryoSerializerUtil.serialize(picturePage);
            kryoRedisTemplate.opsForValue().set(key, serializedData, Duration.ofMinutes(10));
            log.debug("保存图片缓存成功: key={}, size={} bytes", key, serializedData.length);
        } catch (Exception e) {
            log.error("保存图片缓存失败: key={}", key, e);
        }
    }

    /**
     * 获取图片分页缓存
     *
     * @param key 缓存键
     * @return 图片分页数据
     */
    public Page<Picture> getPictureCache(String key) {
        try {
            // 使用专门的 Kryo Redis 模板获取 byte[] 数据
            byte[] bytes = kryoRedisTemplate.opsForValue().get(key);
            if (bytes == null) {
                log.debug("缓存未命中: key={}", key);
                return null;
            }

            Page<Picture> result = KryoSerializerUtil.deserialize(bytes);
            log.debug("获取图片缓存成功: key={}, size={} bytes", key, bytes.length);
            return result;
        } catch (Exception e) {
            log.error("获取图片缓存失败: key={}", key, e);
            return null;
        }
    }

    /**
     * 删除图片分页缓存
     *
     * @param key 缓存键
     */
    public void deletePictureCache(String key) {
        try {
            kryoRedisTemplate.delete(key);
            log.debug("删除图片缓存成功: key={}", key);
        } catch (Exception e) {
            log.error("删除图片缓存失败: key={}", key, e);
        }
    }

    /**
     * 清除所有图片分页缓存
     * 当有新图片上传或审核状态变更时调用
     */
    public void clearAllPictureCache() {
        try {
            // 删除所有图片分页缓存的Redis键
            Set<String> keys = kryoRedisTemplate.keys("picture:listPictureByPage:*");
            if (!keys.isEmpty()) {
                kryoRedisTemplate.delete(keys);
                log.info("清除Redis图片缓存成功，共删除{}个缓存项", keys.size());
            }
        } catch (Exception e) {
            log.error("清除Redis图片缓存失败", e);
        }
    }
}
